/**
 *******************************************************************************
 * @FileName  : tlsr825x_flash.c
 * @Author    : GaoQiu
 * @CreateDate: 2020-09-10
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#include "tlsr825x_flash.h"
//#include "tlsr825x_systimer.h"
#include "utils/critical.h"

static inline void MSPI_Start(void)
{
	MSPI->CTRL = MSPI_CTRL_CS;
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	//delay_us(1);
	MSPI->CTRL = 0x00;
}

static inline void MSPI_Stop(void)
{
	MSPI->CTRL = MSPI_CTRL_CS;
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
}

static inline void MSPI_WriteCmd(uint8_t cmd)
{
	MSPI->CTRL = cmd;
	while(MSPI->CTRL & MSPI_CTRL_BUSY);
}

static inline void MSPI_WriteByte(uint8_t dat)
{
	MSPI->DATA = dat;
	while(MSPI->CTRL & MSPI_CTRL_BUSY);
}

static inline uint8_t MSPI_ReadByte(void)
{
	uint8_t data = MSPI->DATA;
	while(MSPI->CTRL & MSPI_CTRL_BUSY);
	return data;
}

RAM_CODE void FLASH_SendCmd(uint8_t cmd)
{
	MSPI_Start();
	MSPI_WriteByte(cmd);
	//MSPI_Stop();
}

RAM_CODE void FLASH_SendAddr(uint32_t addr)
{
	MSPI_WriteByte((uint8_t)(addr>>16));
	MSPI_WriteByte((uint8_t)(addr>>8));
	MSPI_WriteByte((uint8_t)(addr));
}

RAM_CODE uint8_t FLASH_GetCmdState(void)
{
	MSPI_WriteByte(0);
	return MSPI->DATA;
}

RAM_CODE bool_t FLASH_IsBusy(void)
{
	return (FLASH_GetCmdState() & 0x01);
}

RAM_CODE void FLASH_WaitCmdCmpl(void)
{
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	asm("tnop");asm("tnop");asm("tnop");asm("tnop");
	//delay_us(100);

	FLASH_SendCmd(FLASH_CMD_READ_STATUS);

	for(int i = 0; i<10000000; ++i)
	{
		if(!FLASH_IsBusy()){
			break;
		}
	}
	MSPI->CTRL = MSPI_CTRL_CS;
}

/**
 * @brief : erase flash sector(4k).
 * @param : addr    the address of flash.
 * @return: none.
 */
RAM_CODE void FLASH_EraseSector(uint32_t addr)
{
	uint8_t cpu_state = 0;
	ENTER_CRITICAL(cpu_state);

	FLASH_SendCmd(FLASH_CMD_WRITE_ENABLE);

	FLASH_SendCmd(FLASH_CMD_SECT_ERASE);
	FLASH_SendAddr(addr);

	FLASH_WaitCmdCmpl();

	EXIT_CRITICAL(cpu_state);
}

#if 0
/**
 * @brief : erase flash page(256B).
 *          !!! Note: only support P25Q40L.
 * @param : addr    the address of flash.
 * @return: none.
 */
RAM_CODE void FLASH_ErasePage(uint32_t addr)
{
	uint8_t cpu_state = 0;
	ENTER_CRITICAL(cpu_state);

	FLASH_SendCmd(FLASH_CMD_WRITE_ENABLE);
	FLASH_SendCmd(FLASH_CMD_PAGE_ERASE);
	FLASH_SendAddr(addr);

	MSPI->CTRL = MSPI_CTRL_CS;
	FLASH_WaitCmdCmpl();

	EXIT_CRITICAL(cpu_state);
}

/**
 * @brief : erase all flash.
 * @param : none.
 * @return: none.
 */
RAM_CODE void FLASH_EraseAll(void)
{
	uint8_t cpu_state = 0;
	ENTER_CRITICAL(cpu_state);

	FLASH_SendCmd(FLASH_CMD_WRITE_ENABLE);
	FLASH_SendCmd(FLASH_CMD_CHIP_ERASE);

	MSPI->CTRL = MSPI_CTRL_CS;
	FLASH_WaitCmdCmpl();

	EXIT_CRITICAL(cpu_state);
}
#endif

/**
 * @brief : write flash page.
 * @param : addr     the address of flash.
 * @param : pData    Pointer point to data buffer.
 * @param : len      the length of data.
 * @return: none.
 */
RAM_CODE void FLASH_WritePage(uint32_t addr, uint8_t *pData, uint32_t len)
{
	uint8_t cpu_state = 0;
	ENTER_CRITICAL(cpu_state);

	/**
	 * important:  buf must not reside at flash, such as constant string.
	 *             If that case, pls copy to memory first before write
	 */
	FLASH_SendCmd(FLASH_CMD_WRITE_ENABLE);
	FLASH_SendCmd(FLASH_CMD_WRITE);
	FLASH_SendAddr(addr);

	for(uint32_t i = 0; i<len; ++i)
	{
		MSPI_WriteByte(*pData++);
	}
	//MSPI->CTRL = MSPI_CTRL_CS;
	FLASH_WaitCmdCmpl();

	EXIT_CRITICAL(cpu_state);
}

/**
 * @brief : read flash page.
 * @param : addr     the address of flash.
 * @param : pData    Pointer point to data buffer.
 * @param : len      the length of data.
 * @return: none.
 */
RAM_CODE void FLASH_ReadPage(uint32_t addr, uint8_t *pData, uint32_t len)
{
	uint8_t cpu_state = 0;
	ENTER_CRITICAL(cpu_state);

	FLASH_SendCmd(FLASH_CMD_READ);
	FLASH_SendAddr(addr);

	MSPI_WriteByte(0x00); /*!< dummy,  to issue clock. */
	MSPI_WriteCmd(0x0a);  /*!< auto mode. */

	/* Get data */
	for(uint32_t i = 0; i<len; ++i)
	{
		*pData++ = MSPI->DATA;
		while(MSPI->CTRL & MSPI_CTRL_BUSY);
	}

	MSPI_Stop();

	EXIT_CRITICAL(cpu_state);
}

